const std = @import("std");
const re = @cImport({
    @cDefine("PCRE2_CODE_UNIT_WIDTH", "8");
    @cInclude("pcre2.h");
});

const PCRE2_ZERO_TERMINATED = ~@as(re.PCRE2_SIZE, 0);

/// Compiles a regex pattern string and returns a pattern code you can use
/// to match subjects. Returns `null` if something is wrong with the pattern
pub fn compile(needle: []const u8) ?*re.pcre2_code_8 {
    const pattern: re.PCRE2_SPTR8 = &needle[0];
    var errornumber: c_int = undefined;
    var erroroffset: re.PCRE2_SIZE = undefined;

    const regex: ?*re.pcre2_code_8 = re.pcre2_compile_8(pattern, PCRE2_ZERO_TERMINATED, 0, &errornumber, &erroroffset, null);
    return regex;
}

/// Takes in a compiled regexp pattern from `compile` and a string of test which is the haystack
/// and returns the first match from the haystack.
pub fn find(regexp: *re.pcre2_code_8, haystack: []const u8) ?[]const u8 {
    const subject: re.PCRE2_SPTR8 = &haystack[0];
    const subjLen: re.PCRE2_SIZE = haystack.len;

    const matchData: ?*re.pcre2_match_data_8 = re.pcre2_match_data_create_from_pattern_8(regexp, null);
    const rc: c_int = re.pcre2_match_8(regexp, subject, subjLen, 0, 0, matchData.?, null);

    if (rc < 0) {
        return null;
    }

    const ovector = re.pcre2_get_ovector_pointer_8(matchData);
    if (rc == 0) {
        std.debug.print("ovector was not big enough for all the captured substrings\n", .{});
        return null;
    }

    if (ovector[0] > ovector[1]) {
        std.debug.print("error with ovector\n", .{});
        re.pcre2_match_data_free_8(matchData);
        re.pcre2_code_free_8(regexp);
        return null;
    }
    const match = haystack[ovector[0]..ovector[1]]; // First match only
    return match;
}
pub fn find_all(regexp: *re.pcre2_code_8, haystack: []const u8) ![][]const u8 {
    const subject: re.PCRE2_SPTR8 = &haystack[0];
    const subjLen: re.PCRE2_SIZE = haystack.len;

    const matchData: ?*re.pcre2_match_data_8 = re.pcre2_match_data_create_from_pattern_8(regexp, null);
    defer re.pcre2_match_data_free_8(matchData);

    var matches = std.ArrayList([]const u8).init(std.heap.page_allocator);
    defer matches.deinit();

    var offset: re.PCRE2_SIZE = 0;
    while (true) {
        const rc: c_int = re.pcre2_match_8(regexp, subject, subjLen, offset, 0, matchData.?, null);

        if (rc <= 0) {
            break;
        }

        const ovector = re.pcre2_get_ovector_pointer_8(matchData);
        if (ovector[0] > ovector[1]) {
            std.debug.print("error with ovector\n", .{});
            return error.InvalidOvector;
        }

        const match = haystack[ovector[0]..ovector[1]];
        matches.append(match) catch |err| {
            std.debug.print("error appending match: {}\n", .{err});
            return err;
        };

        offset = ovector[1];
    }

    return matches.toOwnedSlice();
}

pub fn replace_all(allocator: std.mem.Allocator, pattern: []const u8, origin: []const u8, replacement: []const u8) ![]u8 {
    const regex = compile(pattern).?;
    const matches = try find_all(regex, origin);
    var result = try allocator.dupe(u8, origin);
    errdefer allocator.free(result); // 确保在出错时释放内存
    // 遍历所有匹配项并进行替换
    for (matches) |match| {
        // 计算替换后的新长度
        const length_diff = if (replacement.len > match.len)
            replacement.len - match.len
        else
            match.len - replacement.len;

        const new_length = if (replacement.len > match.len)
            result.len + length_diff
        else
            result.len - length_diff;

        // 分配新的缓冲区
        const new_buffer = try allocator.alloc(u8, new_length);
        defer allocator.free(new_buffer); // 确保在出错时释放内存

        // 执行替换
        const replaced_length = std.mem.replace(u8, result, match, replacement, new_buffer);

        // 释放旧的结果并更新为新缓冲区
        allocator.free(result);

        // 重新分配一个大小匹配的缓冲区
        result = try allocator.alloc(u8, replaced_length);
        @memcpy(result, new_buffer[0..replaced_length]); // 使用 @memcpy 复制数据
    }

    // 返回最终结果（调用者需要负责释放内存）
    return result;
}
